package com.sql.mysql.sharding.plugin;

import java.util.Properties;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.BaseExecutor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;

import com.sql.mysql.sharding.threadlocal.ShardingThreadLocal;

import lombok.extern.slf4j.Slf4j;

/**
 * 数据库操作性能拦截器,记录耗时
 * 
 * @Intercepts定义Signature数组,因此可以拦截多个,但是只能拦截类型为： Executor ParameterHandler
 *                                              StatementHandler
 *                                              ResultSetHandler
 */
// 拦截BaseExecutor,这样，可以拦截各种类型的Executor实现
@Intercepts(value = {
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }),
		@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class })

})
@Slf4j
public class ShardExecutorInterceptor implements Interceptor {

	public ShardExecutorInterceptor() {
	}

	//
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// 能进来,说明有方法确实命中了,否则不会进来
		// 1)get AutoCommit type
		BaseExecutor baseExecutor = (BaseExecutor) invocation.getTarget();//
		JdbcTransaction jdbcTransaction = (JdbcTransaction) baseExecutor.getTransaction();
		MetaObject metaObject = SystemMetaObject.forObject(jdbcTransaction);
		Object autoCommitObject = metaObject.getValue("autoCommmit");// 第1行完全是MyBatis作者笔误
		if (null == autoCommitObject) {
			autoCommitObject = metaObject.getValue("autoCommit");
		}
		log.info("autoCommit ---> " + (boolean) autoCommitObject);
		ShardingThreadLocal.AutoCommitThreadLocal.set((boolean) autoCommitObject);
		// 2)存储MappedStatement
		Object[] args = invocation.getArgs();
		MappedStatement ms = (MappedStatement) args[0];
		ShardingThreadLocal.MappedStatementThreadLocal.set(ms);
		log.info("set ThreadLocal sqlCommandType ---> " + ms.getSqlCommandType());
		//
		// 4)触发真正的操作
		long start = System.currentTimeMillis();
		Object result = invocation.proceed();
		long end = System.currentTimeMillis();
		// 5)sql audit
		long totalCost = end - start;
		log.debug("totalcost {}", totalCost);
		return result;
	}

	@Override
	public Object plugin(Object target) {
		// 判断要不要封装
		if (target instanceof Executor) {
			return Plugin.wrap(target, this);
		} else {
			return target;
		}
	}

	@Override
	public void setProperties(Properties properties) {

	}

}